package NICE_CORE

import chisel3._
import chisel3.util._

class cim_mvm_2_8bit(save_width:Int) extends Module with Mvm_config_2 with RequireAsyncReset{
  val io = IO(new Bundle{
    val start     = Input(Bool())
    val mvm_done  = Output(Bool())
    val rcbd      = Input(new mvm_IO_2)
    val push_buf  = Input(UInt((ROW_NUM*2).W))
    val save_buf  = Output(UInt((COL_NUM*save_width).W))
  })
  val idle :: clr::mvm :: Nil = Enum(3)
  val state = RegInit(idle)
  switch(state){
    is(idle){   state := Mux(io.start,clr,idle)    }
    is(clr) {   state := mvm   }
    is(mvm) {   state := Mux(io.mvm_done,idle,mvm)}
  }

  val input_buf = io.push_buf.asTypeOf(Vec(ROW_NUM,UInt(2.W)))
  val output_buf = VecInit(Seq.fill(COL_NUM)(0.S((save_width).W)))
  val output_buf_tmp = RegInit(VecInit(Seq.fill(COL_NUM)(0.S((save_width*2).W))))
  val addr = RegInit(UInt(ROW_BW.W),0.U)
  when(state === clr){
    addr := io.rcbd.row_begin
  }.elsewhen(io.mvm_done){   //1.avoid out of range
    addr := 0.U
  }.elsewhen(state===mvm){
    addr := addr + 1.U
  }
  //rom
  val rom = Module(new cim_rom())
  rom.io.a := Mux(addr<ROW_NUM.U,addr,(ROW_NUM-1).U)
  val rom_out_reverse = (rom.io.spo).asTypeOf(Vec(COL_NUM,SInt(4.W)))
  val rom_out = rom_out_reverse.reverse
  val output_en = Wire(Vec(COL_NUM,Bool()))
  val add_num   = Wire(Vec(COL_NUM,SInt((save_width*2).W)))
  for (i <-0 until COL_NUM ){
    output_en(i) := ( i.U >=io.rcbd.col_begin)& (i.U <= io.rcbd.col_end)
    add_num(i) := rom_out(i)

    when(state === clr){
      output_buf_tmp(i) := 0.S
    }.elsewhen(state===mvm){
//      when(output_buf_tmp(i)       >= 127.S){
//        output_buf(i) := 127.S
//      }.elsewhen(output_buf_tmp(i) <= -127.S){
//        output_buf(i) := -127.S
//      }.otherwise {
//        output_buf(i) := output_buf_tmp(i)
//      }
      output_buf_tmp(i) := Mux(output_en(i), output_buf_tmp(i) +
          Mux(input_buf(addr)(1).asBool(), -add_num(i),
          Mux(input_buf(addr)(0).asBool(),  add_num(i), 0.S)), 0.S)
    }.elsewhen(state===idle){
      output_buf_tmp(i) := output_buf_tmp(i)
    }
    output_buf(i) := Mux(output_buf_tmp(i)>=127.S,127.S,Mux(output_buf_tmp(i)<= -127.S,-127.S,output_buf_tmp(i)))
  }
  //val output_buf_reverse = Wire(Vec(COL_NUM,SInt(16.W)))  //2.weight is BigEndian
  //output_buf_reverse := output_buf.reverse
  //io.save_buf := output_buf_reverse.asUInt()
  io.save_buf := output_buf.asUInt()
  io.mvm_done := (addr === io.rcbd.row_end)&(state===mvm)//3.state ==mvm
}
object cim_mvm_2_8bit{
  def apply(start:Bool,rcbd:mvm_IO_2,push_buf:UInt):(Bool,UInt)={
    val inst = Module(new cim_mvm_2_8bit(8))
    inst.io.start := start
    inst.io.rcbd  := rcbd
    inst.io.push_buf := push_buf
    (inst.io.mvm_done,inst.io.save_buf)
  }
}
